package com.demo.security.access.impl;

import java.io.IOException;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.Map;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler;
import org.springframework.security.web.savedrequest.HttpSessionRequestCache;
import org.springframework.security.web.savedrequest.RequestCache;
import org.springframework.security.web.savedrequest.SavedRequest;

import com.demo.common.RegexUtils;
import com.demo.common.StringUtils;
import com.demo.security.access.model.PlatformUser;
import com.demo.security.access.model.SecurityUser;
import com.demo.security.impl.UserDetailsWrapper;

/**
 * 用户登录后根据其所具有的角色来进行页面跳转
 * 
 * @author ryuu.kk
 */
public abstract class RoleAuthenticationSuccessHandler extends SavedRequestAwareAuthenticationSuccessHandler {
	
	/**
	 * 重定向地址
	 */
	public static final String REDIRECT_URL = "backurl";

	/**
	 * 存放角色和url对应关系的Map
	 */
	private LinkedHashMap<String, String> roleUrlMap;
	
	/**
	 * 重置地址
	 */
	private String resetUrl;

	/**
	 * 是否包含应用上下文
	 */
	private boolean includeContext = true;

	/**
	 * request缓存
	 */
	private RequestCache requestCache = new HttpSessionRequestCache();

	@SuppressWarnings("unchecked")
	@Override
	public void onAuthenticationSuccess(HttpServletRequest httpservletrequest,
			HttpServletResponse httpservletresponse,
			Authentication authentication) throws IOException, ServletException {

		SecurityUser user = null;
		// 登陆成功后的登陆记录
		Object principal = authentication.getPrincipal();
		if (principal instanceof UserDetailsWrapper) {
			UserDetailsWrapper<SecurityUser, Object> target = (UserDetailsWrapper<SecurityUser, Object>) principal;
			user = target.getCurrentUser();
		} else if (principal instanceof PlatformUser) {
			PlatformUser<Object> target = (PlatformUser<Object>) principal;
			Object obj = target.getModel();
			if (obj instanceof SecurityUser) {
				user = (SecurityUser) obj;
			}
		}
		
		// 前置登录检验
		preLoginChecker(user, httpservletrequest, httpservletresponse, authentication);
		
//		// 登录成功后，如果请求中包含语言，则设置其语言到session中
//		String requestLocale = httpservletrequest.getParameter("request_locale");
//		if (!Utils.isEmpty(requestLocale)) {
//			httpservletrequest.getSession().setAttribute(Constant.LOCALE_SESSION_KEY, Utils.localeFromString(requestLocale, null));
//		}
		
		String target = null;
		// 重置地址;如验证用户第一次登陆需要修改密码或其他设置时使用
		if (checkReset(authentication)) {
			String contextPath = httpservletrequest.getContextPath();
			String resetUri = resetUrl;
			if (!contextPath.equals("/")) {
				resetUri = contextPath + resetUri;
			}
			httpservletresponse.sendRedirect(httpservletresponse.encodeURL(resetUri));
			return;
		}
		
		SavedRequest savedRequest = requestCache.getRequest(httpservletrequest,	httpservletresponse);
		
		// 重定向到访问页
		if (savedRequest != null) {
			// 清除异常信息
	        clearAuthenticationAttributes(httpservletrequest);

	        // 回到目标页
	        String targetUrl = savedRequest.getRedirectUrl();
	        if (logger.isDebugEnabled()) {
	        	logger.debug("Redirecting to DefaultSavedRequest Url: " + targetUrl);
	        }
	        getRedirectStrategy().sendRedirect(httpservletrequest, httpservletresponse, targetUrl);
			return;
		}
		
		httpservletrequest.getAttribute(REDIRECT_URL);
		// 存在指定重定向地址参数:"backurl", 进行转向
		String backUrl = getBackUrl(httpservletrequest, httpservletresponse); //httpservletrequest.getParameter(REDIRECT_URL);
		if (StringUtils.hasText(backUrl)) {
			getRedirectStrategy().sendRedirect(httpservletrequest, httpservletresponse, httpservletresponse.encodeURL(backUrl));
			return;
		}
       
		// 反向的循环，虽然效率低，但是，这样做是有原因的：
		// roleUrlMap中配置的角色顺序，决定了一个具有多角色用户应该先到哪个地址
		// 控制authentication里的角色顺序很难，需要在授权的时候来实现。但是控制配置文件的顺序则很容易
		// 以微小的性能代价来减低开发和管理的难度，不这样做很可能增加的复杂性反而使性能更低。
		Collection<? extends GrantedAuthority> authCollection = authentication.getAuthorities();
		for (Map.Entry<String, String> entry : roleUrlMap.entrySet()) {
			for (GrantedAuthority auth : authCollection) {
				target = entry.getValue();
				// 通配符匹配
				if (RegexUtils.wildcardMatch(entry.getKey(), auth.getAuthority()) && target != null) {
					if (target.startsWith("/") && includeContext) {
						String contextPath = httpservletrequest.getContextPath();
						if (!contextPath.equals("/")) {
							target = contextPath + target;
						}
					}
					// 由于采用了encodeURL所以，可以实现url重写
					String url = createTargetUrl(target);
					httpservletresponse.sendRedirect(httpservletresponse.encodeURL(url));
					clearAuthenticationAttributes(httpservletrequest);
					return;
				}
			}
		}
		
		// 未定义角色对应页并且没有目标页则使用默认跳转页
        String targetUrlParameter = getTargetUrlParameter();
        if (isAlwaysUseDefaultTargetUrl() || (targetUrlParameter != null 
        		&& StringUtils.hasText(httpservletrequest.getParameter(targetUrlParameter)))) {
            requestCache.removeRequest(httpservletrequest, httpservletresponse);
            super.onAuthenticationSuccess(httpservletrequest, httpservletresponse, authentication);

            return;
        }

	}

	public abstract String getBackUrl(HttpServletRequest httpservletrequest,
			HttpServletResponse httpservletresponse) ;
	/**
	 * 产生跳转的目标地址
	 * @param url 含有占位符的URl
	 * @return 目标URL
	 */
	public abstract String createTargetUrl(String url);

	/**
	 * 创建登录的前置处理方法
	 */
	public abstract void preLoginChecker(SecurityUser user, 
			HttpServletRequest httpservletrequest,
			HttpServletResponse httpservletresponse,
			Authentication authentication) throws ServletException;
	
	/**
	 * 检查用户是否需要重置(包括密码等信息)
	 * 
	 * @param authentication 权限主体
	 * @return 是否重置
	 */
	@SuppressWarnings("unchecked")
	protected boolean checkReset(Authentication authentication) {
		Object obj = authentication.getPrincipal();
		if (obj instanceof UserDetailsWrapper) {
			UserDetailsWrapper<SecurityUser, Object> userDetails = (UserDetailsWrapper<SecurityUser, Object>) obj;
			// 这里是判断是否需要重置到指定页面
			// 使用userDetails接口,取得当前用户, PlatformUser接口,用来获得当前站点的Member at 2013-06-04 by ryuu.kk
			//Boolean reset = userDetails.getModel().getLocked();
			Boolean reset = userDetails.getCurrentUser().getLocked();
			if (reset == null) {
				reset = false;
			}
			return reset;
		}
		return false;
	}

	public void setRoleUrlMap(LinkedHashMap<String, String> roleUrlMap) {
		this.roleUrlMap = roleUrlMap;
	}

	public LinkedHashMap<String, String> getRoleUrlMap() {
		return roleUrlMap;
	}

	public void setIncludeContext(boolean includeContext) {
		this.includeContext = includeContext;
	}

	public boolean isIncludeContext() {
		return includeContext;
	}

	public void setResetUrl(String resetUrl) {
		this.resetUrl = resetUrl;
	}
}
